package unsafe_lru

import (
	"fmt"
	"sync"
	"testing"
)

type String string

func (s String) Len() int64 {
	return int64(len(s))
}

func TestCache_Get(t *testing.T) {
	lru := New(int64(155), nil)
	err := lru.Put("key1", String("1234"))
	if err != nil {
		t.Fatalf("cache put key1=1234 failed")
	}
	if v, ok := lru.Get("key1"); !ok || string(v.(String)) != "1234" {
		t.Fatalf("cache hit key1=1234 failed")
	}
	if _, ok := lru.Get("key2"); ok {
		t.Fatalf("cache miss key2 failed")
	}
}

// TestCache_Operate_Unsafe 演示 unsafe_lru.Cache 在并发环境下的不安全操作
// 这个测试可能会触发 panic 或数据竞争，因为 unsafe_lru.Cache 不是线程安全的
// 注意：Go 的 map 在并发读写时会 panic（Go 1.6+），但需要同时访问同一个键才容易触发
func TestCache_Operate_Unsafe(t *testing.T) {
	lruCache := New(int64(10000), nil)

	// 初始化一些数据
	for i := 0; i < 10; i++ {
		key := fmt.Sprintf("key%d", i)
		lruCache.Put(key, String(fmt.Sprintf("value%d", i)))
	}
	var wg sync.WaitGroup
	// 启动多个 goroutine 进行并发写入
	for i := 0; i < 50; i++ {
		wg.Add(1)
		go func(id int) {
			defer func() {
				if r := recover(); r != nil {
					fmt.Printf("Goroutine %d panic: %v\n", id, r)
				}
				wg.Done()
			}()
			key := fmt.Sprintf("key%d", id%10)
			value := String(fmt.Sprintf("newvalue%d", id))
			err := lruCache.Put(key, value)
			if err != nil {
				fmt.Printf("Put failed: %v\n", err)
			}
		}(i)
	}

	// 启动多个 goroutine 进行并发读取
	for i := 0; i < 50; i++ {
		wg.Add(1)
		go func(id int) {
			defer func() {
				if r := recover(); r != nil {
					fmt.Printf("Goroutine %d panic: %v\n", id, r)
				}
				wg.Done()
			}()
			key := fmt.Sprintf("key%d", id%10)
			value, exists := lruCache.Get(key)
			if exists {
				_ = value // 使用 value 避免编译器优化
			}
		}(i)
	}
	wg.Wait()
	fmt.Printf("Final cache size: %d, keys: %d\n", lruCache.Size(), lruCache.KeysLen())
}

// TestCache_ConcurrentWriteSameKey 专门测试对同一个键的并发写入，更容易触发 panic
// Go 的 map 在并发读写时会 panic，特别是同时访问同一个键时
func TestCache_ConcurrentWriteSameKey(t *testing.T) {
	lruCache := New(int64(10000), nil)

	// 先写入一个键
	lruCache.Put("key1", String("initial"))

	var wg sync.WaitGroup
	panicCount := 0
	var mu sync.Mutex

	// 100 个 goroutine 同时写入同一个键，增加冲突概率
	for i := 0; i < 100; i++ {
		wg.Add(1)
		go func(id int) {
			defer func() {
				if r := recover(); r != nil {
					mu.Lock()
					panicCount++
					mu.Unlock()
					fmt.Printf("Goroutine %d panic: %v\n", id, r)
				}
				wg.Done()
			}()
			// 所有 goroutine 都操作同一个键
			value := String(fmt.Sprintf("value%d", id))
			lruCache.Put("key1", value)
			// 同时进行读取操作
			_, _ = lruCache.Get("key1")
		}(i)
	}

	wg.Wait()
	fmt.Printf("Panic count: %d/100\n", panicCount)
	fmt.Printf("Final cache size: %d, keys: %d\n", lruCache.Size(), lruCache.KeysLen())

	// 注意：即使没有 panic，也存在数据竞争，使用 go test -race 可以检测到
}

// TestCache_ConcurrentDelete 测试并发删除操作，更容易触发问题
func TestCache_ConcurrentDelete(t *testing.T) {
	lruCache := New(int64(10000), nil)

	// 初始化数据
	for i := 0; i < 20; i++ {
		key := fmt.Sprintf("key%d", i)
		lruCache.Put(key, String(fmt.Sprintf("value%d", i)))
	}

	var wg sync.WaitGroup
	panicCount := 0
	var mu sync.Mutex

	// 并发删除和写入
	for i := 0; i < 50; i++ {
		wg.Add(1)
		go func(id int) {
			defer func() {
				if r := recover(); r != nil {
					mu.Lock()
					panicCount++
					mu.Unlock()
					fmt.Printf("Goroutine %d panic: %v\n", id, r)
				}
				wg.Done()
			}()
			key := fmt.Sprintf("key%d", id%20)
			// 删除操作
			lruCache.Delete(key)
			// 立即重新写入
			lruCache.Put(key, String(fmt.Sprintf("newvalue%d", id)))
			// 读取操作
			_, _ = lruCache.Get(key)
		}(i)
	}

	wg.Wait()
	fmt.Printf("Panic count: %d/50\n", panicCount)
}
